import json
import os
import time
import typing

from pya2l.parser import A2lParser

from .common import tool_function
from .common.datatypes import *
from .common.hex_io import *
from .xcp_slave import XcpSlave
from .common.logger import get_logger


class XcpBridge:
    def __init__(self, init_info: XCPBridgeInitData) -> None:
        self._init_info = init_info
        self._xcp_slave:XcpSlave = None
    
    @staticmethod
    def export_xcp_bridge_metadata(
        metadata:XCPBridgeMetaData, 
        out_path:typing.Optional[str] = None, 
        out_file_prefix:typing.Optional[str] = None) -> bool:
        """将XCP Bridge元数据导出到路径中，导出的文件有三个，分别是 .a2l .hex

        Args:
            metadata (XcpBridgeMetaData): 元数据结构
            out_path (typing.Optional[str], optional): 导出的路径. Defaults to None.
            out_file_prefix (typing.Optional[str], optional): 导出的前缀. Defaults to "".

        Returns:
            bool: _description_
        """
        """
        1. 构建 hex 文件
        2. 构建 a2l 文件
        3. 保存的指定的路径，如果存在则覆盖
        
        
        导出 hex 文件流程
        1. 基于元数据的观测变量列表，根据每个变量的数据长度，生成对应的默认数据列表，计算
        """
        # 打印数据
        get_logger().info("Exporting XCP Bridge metadata...")
        
        # 构建常用数据
        mearsure_map_length = {
            XCPDataType.UBYTE: 1,
            XCPDataType.SBYTE: 1,
            XCPDataType.UWORD: 2,
            XCPDataType.SWORD: 2,
            XCPDataType.ULONG: 4,
            XCPDataType.SLONG: 4,
            XCPDataType.A_UINT64: 8,
            XCPDataType.A_INT64: 8,
            XCPDataType.FLOAT32_IEEE: 4,
            # XCPDataType.FLOAT64_IEEE: 8,
        }
        calibration_map_length = {
            XCPRecordLayout.Scalar_BOOLEAN :8,
            XCPRecordLayout.Scalar_UBYTE : 8,
            XCPRecordLayout.Scalar_SBYTE : 8,
            XCPRecordLayout.Scalar_UWORD : 8,
            XCPRecordLayout.Scalar_SWORD : 8,
            XCPRecordLayout.Scalar_ULONG : 8,
            XCPRecordLayout.Scalar_SLONG : 8,
            XCPRecordLayout.Scalar_A_UINT64 : 8,
            XCPRecordLayout.Scalar_A_INT64 : 8,
            XCPRecordLayout.Scalar_FLOAT32_IEEE : 8,
            # XCPRecordLayout.Scalar_FLOAT64_IEEE : 8,
        }
        
        # 构建辅助变量
        hex_out_path = ""
        a2l_out_path = ""
        actual_out_path = ""
        actual_out_prefix = ""
        if (isinstance(out_path, str)):
            if (not os.path.exists(out_path)):
                os.makedirs(out_path)
            actual_out_path = out_path
        else:
            actual_out_path = os.getcwd()
        if (isinstance(out_file_prefix, str)):
            actual_out_prefix = out_file_prefix
        else:
            # 当前时间作为默认前缀
            actual_out_prefix = f"xcp_bridge_{time.strftime('%Y%m%d%H%M%S', time.localtime())}"
        hex_out_path = os.path.join(actual_out_path, actual_out_prefix + ".hex")
        a2l_out_path = os.path.join(actual_out_path, actual_out_prefix + ".a2l")
        
        # 读取 a2l template 文件
        lib_root_path = os.path.join(os.path.dirname(__file__))
        a2l_json_path = os.path.abspath(os.path.join(lib_root_path, "template", "example_xcp.json"))
        a2l_json_d:dict = json.loads(open(a2l_json_path, "r", encoding="utf-8").read())
        a2l_mearsurement_list:list = a2l_json_d["PROJECT"]["MODULE"][0]["MEASUREMENT"]
        a2l_characteristic_list:list = a2l_json_d["PROJECT"]["MODULE"][0]["CHARACTERISTIC"]
        a2l_memory_segment_data:dict = a2l_json_d["PROJECT"]["MODULE"][0]["MOD_PAR"]["MEMORY_SEGMENT"][0]  # 放置观测数据的
        a2l_memory_segment_code:dict = a2l_json_d["PROJECT"]["MODULE"][0]["MOD_PAR"]["MEMORY_SEGMENT"][1]  # 放置标定数据的
        a2l_ip_param:dict = a2l_json_d["PROJECT"]["MODULE"][0]["IF_DATA"][0]["Blob"][4]["Generic"]["Element"][3]["String"]
        
        # 修改 IP
        get_logger().debug(f"XCP Bridge IP: {metadata.xcp_if_parameters.ip}")
        a2l_ip_param["Value"] = metadata.xcp_if_parameters.ip if (metadata.xcp_if_parameters.ip) else "127.0.0.1"
        get_logger().debug(f"{a2l_ip_param}")
        
        # 构建 hex 文件
        memery_data_list = []
        mearsurement_address_start = 0x00000000
        calibration_address_start = 0x00000000
        current_address = mearsurement_address_start
        data_list = []
        a2l_memory_segment_code["Address"]["Value"] = str(mearsurement_address_start)
        a2l_memory_segment_code["Size"]["Value"] = "32"
        for mearsurement in metadata.mearsurements:
            data_length:int = mearsure_map_length[mearsurement.data_type]
            data_list.extend([0x00]*data_length)
            calibration_address_start += data_length
            
            # 添加 a2l 中的 mearsurement 变量
            mearsurement.address = current_address
            a2l_mearsurement_list.append(tool_function.XCPA2lMearsurement_to_a2l_dict(mearsurement))
            current_address += data_length
            
        
        # # 测试代码 遍历生成 0x00 ~ 0xFF 的数据
        # temp_data_map = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]
        # for i in range(len(data_list)):
        #     data_list[i] = temp_data_map[(i % 5)]
        # # 测试代码结束

        memery_data_list.append(MemoryData(
            address_start=mearsurement_address_start,
            data_list=data_list,
        ))
        
        # 设置 calibration_address_start 是 0x10000 的倍数
        calibration_address_start = int(((calibration_address_start // 0x10000) + 1) * 0x10000)
        current_address = calibration_address_start
        a2l_memory_segment_data["Address"]["Value"] = str(calibration_address_start)
        # a2l_memory_segment_data["Size"]["Value"] = "32"
        data_list.clear()
        for calibration in metadata.characteristics:
            data_length:int = calibration_map_length[calibration.record_layout]
            data_list.extend([0x00]*data_length)
            calibration.address = current_address
            a2l_characteristic_list.append(tool_function.XCPA2lCharacteristic_to_a2l_dict(calibration))
            current_address += data_length
        # code 段是标定区域，要设置长度为 0x20 的倍数，否则 INCA 不计算 CRC
        code_len = int(0x20 * ((len(data_list) // 0x20) + 1))
        a2l_memory_segment_data["Size"]["Value"] = str(code_len)
            
        # # 测试代码 遍历生成 0x00 ~ 0xFF 的数据
        # for i in range(len(data_list)):
        #     data_list[i] = temp_data_map[(i % 5)]
        # # 测试代码结束
        
        
        memery_data_list.append(MemoryData(
            address_start=calibration_address_start,
            data_list=data_list
        ))
        
        # 生成其他的 a2l 数据
        # 生成 IP 地址
        
        # 生成 hex 文件
        HexIO.build_hex_file(memory_data_list=memery_data_list, file_path=hex_out_path)
        
        # 生成 a2l 文件
        a2l_parser = A2lParser()
        a2l_json_s = json.dumps(a2l_json_d, indent=2)
        a2l_root = a2l_parser.tree_from_json(json.dumps(a2l_json_d).encode("utf-8"))
        a2l_bytes = a2l_parser.a2l_from_tree(a2l_root, indent=2)
        with open(a2l_out_path, 'wb') as f:
            f.write(a2l_bytes)
        
        # 打印日志
        get_logger().info(f"生成 a2l 文件: {a2l_out_path}")
        get_logger().info(f"生成 hex 文件: {hex_out_path}")
        return True
    
    def start(self) -> None:
        get_logger().setLevel(self._init_info.logger_level)
        self._xcp_slave = XcpSlave(
            "xcp_bridge_demo.a2l", 
            "xcp_bridge_demo.hex",
            self._init_info.cb_on_read_mearsurement,
            self._init_info.cb_on_calibration_changed,
            )
        self._xcp_slave.spin()
        pass
    
    def stop(self) -> None:
        if (not isinstance(self._xcp_slave, XcpSlave)):
            return
        self._xcp_slave.stop()
        self._xcp_slave = None